package ${packageName}.security;

import ${packageName}.config.TenantSecurityProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 多租户用户认证服务
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class TenantAuthenticationService {

    private final TenantSecurityProperties securityProperties;
    private final PasswordEncoder passwordEncoder;
    private final Map<String, String> encodedPasswords = new ConcurrentHashMap<>();

    public UserDetails authenticate(String tenantId, String username, String rawPassword) {
        TenantSecurityProperties.User user = findUser(tenantId, username);

        String cacheKey = cacheKey(tenantId, username);
        String encodedPassword = encodedPasswords.computeIfAbsent(cacheKey, key -> encode(user.getPassword()));

        if (!passwordEncoder.matches(rawPassword, encodedPassword)) {
            log.warn("租户 [{}] 用户 [{}] 认证失败: 密码不匹配", tenantId, username);
            throw new BadCredentialsException("用户名或密码错误");
        }

        return buildUserDetails(username, encodedPassword, user);
    }

    public UserDetails loadUser(String tenantId, String username) {
        TenantSecurityProperties.User user = findUser(tenantId, username);
        String cacheKey = cacheKey(tenantId, username);
        String encodedPassword = encodedPasswords.computeIfAbsent(cacheKey, key -> encode(user.getPassword()));
        return buildUserDetails(username, encodedPassword, user);
    }

    private String encode(String storedPassword) {
        if (storedPassword == null || storedPassword.trim().isEmpty()) {
            throw new BadCredentialsException("密码未配置");
        }
        if (isBcrypt(storedPassword)) {
            return storedPassword;
        }
        return passwordEncoder.encode(storedPassword);
    }

    private boolean isBcrypt(String value) {
        return value.startsWith("$2a$") || value.startsWith("$2b$") || value.startsWith("$2y$");
    }

    private String cacheKey(String tenantId, String username) {
        return tenantId + ":" + username;
    }

    private TenantSecurityProperties.User findUser(String tenantId, String username) {
        TenantSecurityProperties.Tenant tenant = securityProperties.getTenants().stream()
            .filter(item -> Objects.equals(item.getTenantId(), tenantId))
            .findFirst()
            .orElseThrow(() -> new BadCredentialsException("租户不存在"));

        return tenant.getUsers().stream()
            .filter(item -> Objects.equals(item.getUsername(), username))
            .findFirst()
            .orElseThrow(() -> new BadCredentialsException("用户名或密码错误"));
    }

    private UserDetails buildUserDetails(String username, String encodedPassword, TenantSecurityProperties.User user) {
        List<SimpleGrantedAuthority> authorities = user.getRoles().stream()
            .map(SimpleGrantedAuthority::new)
            .collect(Collectors.toList());

        return User.withUsername(username)
            .password(encodedPassword)
            .authorities(authorities)
            .build();
    }
}
